/**
* @file pcqc.h
* @author Alberto Basso, Luca Tumelero
* @brief Implements the main logic of the program
*/

#ifndef PCQC_H
#define PCQC_H

#include "registration.h"
#include "componentSelection.h"
#include "componentMatch.h"
#include "component.h"
#include "segmentDifferences.h"
#include "check.h"
#include "ardoran.h"

// QT
#include <qiterator.h>
#include <QMap>
#include <QString>
#include <QColor>

// PCL
#include <pcl/filters/extract_indices.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/common/geometry.h>
#include <pcl/sample_consensus/sac_model_perpendicular_plane.h>

using namespace pcl;
using namespace std;
// NOTE: the class can easily be converted to use std libraries instead of QT, for QMap (std::map) as for QString (std::string) and color (triple of integers).

/**
 * @brief Implements the main logic of the application.
 *
 */
class Pcqc
{
public:

    //CONSTRUCTOR AND LOADERS
/**
 * @brief Default constructor.
 *
 */
    Pcqc();
    /**
     * @brief Load the reference cloud.
     *
     * @param[in] path Path to the cloud to load.
     * @return bool True if the cloud was loaded correctly.
     */
    bool loadTargetCloud(QString path);
    /**
     * @brief Load the sample cloud.
     *
     * @param[in] path Path to the cloud to load.
     * @return bool True if the cloud was loaded correctly.
     */
    bool loadSourceCloud(QString path);
    /**
     * @brief Load the reference cloud for the Ardoran PCB.
     *
     * @param[in] path Path to the cloud to load.
     * @return bool True if the cloud was loaded correctly.
     */
    bool loadArdoranReferenceCloud(QString path);
    /**
     * @brief Load the sample cloud for the Ardoran PCB.
     *
     * @param[in] path Path to the cloud to load.
     * @return bool True if the cloud was loaded correctly.
     */
    bool loadArdoranSampleCloud(QString path);
    //GETTERS
    /**
     * @brief Get a pointer to the reference cloud.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the reference cloud.
     */
    PointCloud<PointXYZRGB>::Ptr getTargetCloud();
    /**
     * @brief Get a pointer to the sample cloud.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the sample cloud.
     */
    PointCloud<PointXYZRGB>::Ptr getSourceCloud();
    /**
     * @brief Get a pointer to the registered sample cloud.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the registered sample cloud.
     */
    PointCloud<PointXYZRGB>::Ptr getRegisteredCloud();
    /**
     * @brief Get a pointer to the differece cloud between reference and sample clouds.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the difference cloud.
     */
    PointCloud<PointXYZRGB>::Ptr getDifferenceCloud();
    /**
     * @brief Get a pointer to the reference cloud for the Ardoran PCB.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the reference cloud for the Ardoran PCB.
     */
    PointCloud<PointXYZRGB>::Ptr getArdoranReferenceCloud();
    /**
     * @brief Get a pointer to the current sample cloud for the Ardoran PCB.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the current sample cloud for the Ardoran PCB.
     */
    PointCloud<PointXYZRGB>::Ptr getArdoranSampleCloud();
    /**
     * @brief Get a pointer to the current registered sample cloud for the Ardoran PCB.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the current registered sample cloud for the Ardoran PCB.
     */
    PointCloud<PointXYZRGB>::Ptr getArdoranRegisteredCloud();
    /**
     * @brief Get a pointer to the QColor object representing the color of the point of the reference cloud specified by the index.
     *
     * @param[in] pointIndex The index of the point.
     * @return QColor* Pointer to the QColor object representing the color of the point.
     */
    QColor* getPointColor(int pointIndex);
    /**
     * @brief Get a pointer to the last selected target component cloud.
     *
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the cloud representing last selected target component.
     */
    PointCloud<PointXYZRGB>::Ptr getNewComponentCloud();
    /**
     * @brief Get a pointer to a reference component cloud.
     *
     * @param[in] componentName Name of the reference component requested.
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the reference component cloud requested.
     */
    PointCloud<PointXYZRGB>::Ptr getTargetComponentCloud(QString componentName);
    /**
     * @brief Get a pointer to a sample component cloud.
     *
     * @param[in] componentName Name of the sample component requested.
     * @return PointCloud<PointXYZRGB>::Ptr Pointer to the sample component cloud requested.
     */
    PointCloud<PointXYZRGB>::Ptr getSourceComponentCloud(QString componentName);
    /**
     * @brief Get the target component list.
     *
     * @return QMap<QString, Component> The target component list.
     */
    QMap<QString, Component>* getTargetComponentsList();
    /**
     * @brief Get the current target component list generated by the last automatic search for component.
     *
     * @return QMap<QString, Component> The current target component list generated by the last automatic search for component.
     */
    QMap<QString, Component>* getCurrentTargetComponentsList();
    /**
     * @brief Get the source component list.
     *
     * @return QMap<QString, Component> The source component list.
     */
    QMap<QString, Component>* getSourceComponentsList();
    /**
     * @brief Get the checks list.
     *
     * @return QMap<QString, Check> The checks list.
     */
    QMap<QString, Check>* getChecksList();
    /**
     * @brief Verify that the registration has been alredy performed on the current couple of clouds.
     *
     * @return bool TRUE if the registration has been alredy performed on the current couple of clouds.
     */
    bool isRegistrationAlreadyPerformed();
    /**
     * @brief Verify that the registration has been alredy performed on the current couple of clouds for the Ardoran PCB.
     *
     * @return bool TRUE if the registration has been alredy performed on the current couple of clouds for the Ardoran PCB..
     */
    bool isArdoranRegistrationAlreadyPerformed();

    //SETTERS
    /**
     * @brief Set the threshold for the euclidean clustering segmentation of the selected component.
     *
     * @param[in] threshold The threshold for the euclidean clustering segmentation of the selected component.
     */
    void setClusterSegThreshold(double threshold);
    /**
     * @brief Set the threshold (0-255) for the color segmentation of the selected component.
     *
     * @param[in] threshold The threshold (0-255) for the color segmentation of the selected component.
     */
    void setColorSegThreshold(int threshold);
    /**
     * @brief Set the threshold for the euclidean clustering for the automatic segmentation.
     *
     * @param[in] threshold The threshold for the euclidean clustering for the automatic segmentation.
     */
    void setClusterSegAutoThreshold(double threshold);
    /**
     * @brief Set the minimum number of points in a cluster for the automatic clustering.
     *
     * @param[in] points The minimum number of points in a cluster for the automatic clustering.
     */
    void setClusterSegMinPoints(int points);
    /**
     * @brief Set the maximum number of points in a cluster for the automatic clustering.
     *
     * @param[in] points The maximum number of points in a cluster for the automatic clustering.
     */
    void setClusterSegMaxPoints(int points);
    /**
     * @brief Set the score and distance thresholds for the automatic differences segmentation.
     *
     * @param[in] scoreThresh The score threshold.
     * @param[in] distanceThresh The distance threshold.
     */
    void setScoreDistanceThreshold(float scoreThresh, float distanceThresh);

    //FUNCTIONS
    /**
     * @brief Color in r, g, b the point of the input cloud specified by indices.
     *
     * @param[in] cloud Pointer to the cloud to paint.
     * @param[in] indices The indices of the points to paint.
     * @param[in] r The red value (0, 255)
     * @param[in] g The green value (0, 255)
     * @param[in] b The blue value (0, 255)
     */
    void colorIndices(PointCloud<PointXYZRGB>::Ptr cloud, PointIndices::Ptr indices,int r,int g, int b);
    /**
     * @brief Select a component with the specified thresholds, and color it in green in the newComponentCloud.
     *
     * @param[in] selectedPointIndex The index of the clicked point.
     */
    void componentSelection(int selectedPointIndex);
    /**
     * @brief // Segment the new component from the target cloud with the newComponentPointIndices and save it in the newComponentCloud.
     *
     * @return bool True if segmentation has been completed succesfully.
     */
    bool componentSegmentation();
    /**
     * @brief Add segmented component to target components list.
     *
     * @param[in] componentName The name of the new component to be added to the target component list.
     * @return bool True if the component has been added succesfully
     */
    bool componentSave(QString componentName);
    /**
     * @brief Delete a component from the target components list.
     *
     * @param componentName The name of the component to be deleted.
     * @return bool True if the component has been succesfully deleted.
     */
    bool componentDelete(QString componentName); //
    /**
     * @brief Delete all components from targetComponentsList, sourceComponentsList and all checks from checkList.
     *
     */
    void removeAllComponents();
    /**
     * @brief Add check to the check list.
     *
     * @param checkName The name of the check to be added.
     * @param componentName The name of the component on which the check will be performed.
     * @param type The type of check to be performed.
     * @param threshold The threshold value of the check, has different meaning based on the check type.
     * @return bool True if the check has been succesfully added.
     */
    bool addCheck(QString checkName, QString componentName, CheckType type, float threshold);
    /**
     * @brief Delete a check from the check list.
     *
     * @param checkName The name of the check to be deleted.
     * @return bool True if the check has been succesfully deleted.
     */
    bool deleteCheck(QString checkName);
    /**
     * @brief Performs registration from source to target cloud.
     *
     */
    void registration();
    /**
     * @brief Performs registration from sample to reference cloud for the Ardoran PCB.
     *
     */
    void registrationArdoran();
    /**
     * @brief Apply a voxel filter with the specified leaf size the input cloud.
     *
     * @param input The cloud to be filtered.
     * @param leafSize The value fo the leaf size of the voxel filter.
     * @return PointCloud<PointXYZRGB>::Ptr Pointe to the filtered cloud.
     */
    PointCloud<PointXYZRGB>::Ptr voxelCloud (PointCloud<PointXYZRGB>::Ptr input, double leafSize);
    /**
     * @brief Remove the main plane from the input cloud using SACSegmentation.
     *
     * @param[in] source Pointer to the cloud to be processed.
     */
    void segmentation (PointCloud<PointXYZRGB> ::Ptr source);
    /**
     * @brief Removes outliers from the input cloud using StatisticalOutlierRemoval.
     *
     * @param[in] cloud Pointer to the cloud to be processed.
     */
    void removeOutliers(PointCloud<PointXYZRGB>::Ptr cloud);
    /**
     * @brief Finds components in the source cloud corresponding to the componends of the target cloud specified in targetComponentsList and insert them in sourceComponentList.
     *
     * @return int  The number of source cloud components found.
     */
    int findSourceComponents();
    /**
     * @brief Automatically find component in the target cloud based on euclidean clustering segmentation.
     *
     * @return int The numeber of target cloud components found.
     */
    int findTargetComponents();
    /**
     * @brief Performs a single check basing on the type specified. It is used by performChecks().
     *
     * @param source The source cloud component.
     * @param target The target cloud component.
     * @param transformation The rigid transformation form the source to the reference component.
     * @param type The type of check to be performed.
     * @param threshold The threshold value of the check, has different meaning based on the check type.
     * @return bool True if the check has been succesfully performed.
     */
    bool singleCheck(Component source, Component target, Eigen::Matrix4f transformation, CheckType type, float threshold);
    /**
     * @brief Performs all the checks stored in checkList.
     *
     * @return QMap<QString, bool> A dictionary that contains the couples checkName-resultOfTheCheck
     */
    QMap<QString, bool> performChecks();
    /**
     * @brief Performs difference analysis between the source and the target cloud when no component has been specified.
     *
     * @return int The number of differences based on the number of different clusted found.
     */
    int segmentDifferences();
    /**
     * @brief Update the target component list
     *
     */
    void updateTargetComponentList();
    /**
     * @brief Performs base connection check on Ardoran PCB ribbon cable.
     *
     */
    bool checkBaseArdoran();
    /**
     * @brief Performs side connection check on Ardoran PCB ribbon cable.
     *
     */
    bool checkSideArdoran();

private:
    // LOCAL VARIABLES
    PointCloud<PointXYZRGB>::Ptr sourceCloud;  /**< the new cloud to be registered and checked. */
    PointCloud<PointXYZRGB>::Ptr targetCloud;  /**< the reference cloud that is correct in every detail. */
    PointCloud<PointXYZRGB>::Ptr registeredCloud; /**< the source cloud registered to the target cloud. */
    PointCloud<PointXYZRGB>::Ptr differenceCloud; /**< the output cloud from differences segmentation. */
    PointCloud<PointXYZRGB>::Ptr ardoranReferenceCloud; /**< reference cloud for Ardoran PCB. */
    PointCloud<PointXYZRGB>::Ptr ardoranSampleCloud;  /**< sample cloud for Ardoran PCB. */
    PointCloud<PointXYZRGB>::Ptr ardoranRegisteredCloud;  /**< registered cloud for Ardoran PCB. */


    double cluThreshold;  /**< the euclidean threshold for the manual clustering. */
    int colThreshold;  /**< the color threshold (0-255) of tolerance for the manual color segmentation. */
    double cluThresholdAuto; /**< the euclidean threshold for the automatic clustering. */
    int minPoints; /**< the minimum number of points in a cluster for the automatic clustering. */
    int maxPoints; /**< the maximum number of points in a cluster for the automatic clustering. */
    PointCloud<PointXYZRGB>::Ptr newComponentCloud; /**< temp cloud for the component definition */
    PointIndices::Ptr newComponentPointIndices; /**< temp point indices for the component definition */
    int lastClickedPointIndex;  /**< the index of the last point clicked (used to define the component selection). */
    QMap<QString, Component> targetComponentsList; /**< dictonary that maps a name of a component with a point indices of the target cloud that define that component. */
    QMap<QString, Component> currentTargetComponentsList; /**< dictonary that maps a name of a component with a point indices of the target cloud that define that component, generated by the last automatic search for component. */
    QMap<QString, Component> sourceComponentsList; /**< list of the components found in the source cloud corresponding to the componentsList ones. */
    QMap<QString, Check> checksList; /**< list of the checks to be performed. */
    bool registrationAlreadyPerformed;  /**< true if registration has already been performed and not needed again. */
    bool registrationAlreadyPerformedArdoran;  /**< true if registration has already been performed and not needed again. */
    float scoreThreshold,distanceThreshold;  /**< thresholds to use with generic checks */
};

#endif // PCQC_H
